/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.services;

import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang.ArrayUtils;
import org.xptracker.domain.State;
import org.xptracker.domain.StateInfo;
import org.xptracker.domain.Story;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class StoryState {
  private static Map<State, Map<String, String>> verbCache = new HashMap<State, Map<String, String>>();

  public static State[] getNextActionStates(Story s) {
    return getNextActionStates(s.getState());
  }

  public static State[] getNextActionStates(State state) {
    final State[] actionStates = getStandardNextActionStates(state);
    State[] allStates = new State[actionStates.length + 1];
    System.arraycopy(actionStates, 0, allStates, 0, actionStates.length);
    allStates[allStates.length - 1] = State.inQueue;
    return allStates;
  }

  //todo Put these into the database as state transitions
  private static State[] getStandardNextActionStates(State state) {
    switch(state) {
      case dreaming:
        return new State[] {State.inProcess, State.rejected};
      case inQueue:
        return new State[] {State.inProcess, State.dreaming};
      case inProcess:
        return new State[] {State.developed, State.blocked, State.dreaming};
      case blocked:
        return new State[] {State.inProcess};
      case rejected:
        return new State[] {State.dreaming};
      case developed:
        return new State[] {State.inProcess, State.tested};
      case tested:
        return new State[] {State.released, State.inProcess};
      case released:
        return new State[] {State.inProcess};
      default:
        return new State[0];
    }
  }

  public static String getRealizationVerb(State from, State to) {
    Map<String, String> verbs = verbCache.get(to);
    if (verbs == null) {
      verbs = new HashMap<String, String>();
      String realizationStates = getInfo(to).realizationVerbs();
      String[] verbChunks = realizationStates.split(",");
      for(String chunk: verbChunks) {
        String[] stateVerb = chunk.split(":");
        verbs.put(stateVerb[0], stateVerb[1]);
      }
      verbCache.put(to, verbs);
    }
    return verbs.get(from.toString());
  }

  public static StateInfo getInfo(State state) {
    try {
      return State.class.getField(state.toString()).getAnnotation(StateInfo.class);
    } catch (NoSuchFieldException e) {
      LogFactory.getLog(StoryState.class).error("No info on state " + state);
      return null;
    }
  }

  public static boolean setNextState(StoryStateVisitor visitor, Story story, State nextState, boolean ignoreNextState) {
    if (!ignoreNextState && !Arrays.asList(getNextActionStates(story)).contains(nextState)) {
      return false;
    }

    switch(nextState) {
      case inQueue:
        if (story.getState() == State.inProcess) {
          if (story.getQueue() != null) {
            story.getQueue().add(0, story);
          }
          visitor.removeOwner();
        }
        break;
      case dreaming:
        if (story.getState() == State.inProcess) {
          visitor.removeOwner();
        }

        if (story.getQueue() != null) {
          visitor.removeFromQueue("Just Dreaming.");
        }
        break;
      case inProcess:
        if (story.getState() != State.blocked) {
          visitor.setCurrentUser();
        }

        if (story.getState() == State.inQueue) {
          visitor.removeFromQueue(MessageFormat.format("Accepted by {0}", story.getOwner().getUsername()));
        }
        break;
      case rejected:
        visitor.addCurrentUserAsTracker();
        visitor.removeFromQueue("Rejected.");
        visitor.removeOwner();
        break;
    }
    story.setState(nextState);
    return true;
  }

  public static interface StoryStateVisitor {
    void setCurrentUser();
    void removeOwner();
    void addCurrentUserAsTracker();
    void removeFromQueue(String reason);
  }
}
